package ace.module.account.core.impl.service.authentication.handler.impl.password;

import ace.cmp.json.api.JsonService;
import ace.module.account.api.enums.IamAccountPassportPassportTypeEnum;
import ace.module.account.api.model.auth.AuthenticationGrantType;
import ace.module.account.api.model.auth.dto.AuthenticationResult;
import ace.module.account.api.model.auth.input.GenericAuthenticationInputImpl;
import ace.module.account.api.model.auth.input.params.AuthenticationParamsPassword;
import ace.module.account.core.impl.dao.entity.IamAccountPassword;
import ace.module.account.core.impl.manager.IamAccountPassportManager;
import ace.module.account.core.impl.manager.IamAccountPasswordManager;
import ace.module.account.core.impl.service.authentication.handler.impl.AbstractAuthenticationHandler;
import ace.module.account.core.impl.service.authentication.handler.model.input.AuthenticationHandlerContextInput;
import ace.module.captcha.api.CaptchaApplicationApi;
import ace.module.captcha.api.CaptchaImageApi;
import ace.module.captcha.api.model.dto.CaptchaApplicationDto;
import java.util.Arrays;
import java.util.List;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

/**
 * @author caspar
 * @date 2023/3/16 9:49 账号与密码认证，账号支持类型：用户名、电子邮件、手机号码
 */
@Component
public class PasswordAuthenticationHandler
    extends AbstractAuthenticationHandler<
        AuthenticationParamsPassword,
        GenericAuthenticationInputImpl<AuthenticationParamsPassword>> {

  private final IamAccountPasswordManager iamAccountPasswordManager;
  private final IamAccountPassportManager iamAccountPassportManager;
  private final CaptchaImageApi captchaImageApi;
  private final CaptchaApplicationApi captchaApplicationApi;
  private final PasswordEncoder passwordEncoder;
  private static final String CAPTCHA_IMAGE_APP_KEY_SUFFIX = "_usernameAuth";

  protected PasswordAuthenticationHandler(
      JsonService jsonService,
      IamAccountPasswordManager iamAccountPasswordManager,
      IamAccountPassportManager iamAccountPassportManager,
      CaptchaImageApi captchaImageApi,
      CaptchaApplicationApi captchaApplicationApi,
      PasswordEncoder passwordEncoder) {
    super(AuthenticationGrantType.ALL_PASSPORT_PASSWORD.getCode(), jsonService);
    this.iamAccountPasswordManager = iamAccountPasswordManager;
    this.iamAccountPassportManager = iamAccountPassportManager;
    this.captchaImageApi = captchaImageApi;
    this.captchaApplicationApi = captchaApplicationApi;
    this.passwordEncoder = passwordEncoder;
  }

  @Override
  protected AuthenticationResult doAuthentication(
      AuthenticationHandlerContextInput<AuthenticationParamsPassword> handlerContext) {

    this.checkCaptchaImage(handlerContext);

    List<IamAccountPassword> accountPasswords = this.getAccountPasswords(handlerContext);

    if (accountPasswords.isEmpty()) {
      return null;
    }

    for (IamAccountPassword accountPassword : accountPasswords) {
      String postPassword = handlerContext.getAuthenticationContext().getBizParams().getPassword();
      if (passwordEncoder.matches(postPassword, accountPassword.getPassword())) {
        return AuthenticationResult.builder().accountId(accountPassword.getIamAccountId()).build();
      }
    }

    return null;
  }

  private List<IamAccountPassword> getAccountPasswords(
      AuthenticationHandlerContextInput<AuthenticationParamsPassword> handlerContext) {
    List<Integer> passportTypes =
        Arrays.asList(
            IamAccountPassportPassportTypeEnum.USERNAME.getCode(),
            IamAccountPassportPassportTypeEnum.EMAIL.getCode(),
            IamAccountPassportPassportTypeEnum.PHONE.getCode());

    Long iamAppId = handlerContext.getAuthenticationContext().getIamApplication().getId();
    String passport = handlerContext.getAuthenticationContext().getBizParams().getPassport();
    String tag = handlerContext.getAuthenticationContext().getTag();

    List<Long> iamAccountPassportAccountIdList = iamAccountPassportManager
        .getAccountIdByIamAppIdAndTagAndPassportAndPassportTypes(iamAppId, tag, passport, passportTypes);

    List<IamAccountPassword> results =
        iamAccountPasswordManager.getByAccountIdIn(iamAccountPassportAccountIdList);

    return results;
  }

  private void checkCaptchaImage(
      AuthenticationHandlerContextInput<AuthenticationParamsPassword> handlerContext) {
    String captchaAppKey =
        handlerContext.getAuthenticationContext().getIamApplication().getKey()
            + CAPTCHA_IMAGE_APP_KEY_SUFFIX;
    CaptchaApplicationDto captchaApplicationDto =
        captchaApplicationApi.getByKey(captchaAppKey).check();
    if (captchaApplicationDto == null) {
      return;
    }
    Long captchaItemId =
        handlerContext.getAuthenticationContext().getBizParams().getCaptchaItemId();
    String code = handlerContext.getAuthenticationContext().getBizParams().getCode();
    captchaImageApi.verify(captchaItemId, code).check();
  }
}
